/** @jsxImportSource @emotion/react */
import { jsx,css, SerializedStyles } from "@emotion/react";
import * as React from "react";
import { animated, useSpring } from "@react-spring/web";
// import PropTypes from "prop-types";
import { useMeasure } from "./Hooks/use-measure";
import { useTheme } from "./Theme/Providers";
import { Text } from "./Text";
// import scrollIntoView from "scroll-into-view-if-needed";

/* 类似包可用的对比：
@radix-ui/react-collapsible
放弃"react-toastnotes"改用 react-toastify
@radix-ui/react-visually-hidden
@radix-ui/react-accessible-icon
@radix-ui/react-portal
@radix-ui/react-dialog
@radix-ui/react-accordion
@radix-ui/popper
@radix-ui/react-label
* */

export function useCollapse(defaultShow: boolean = false, press:boolean=false) {
    const [show, setShow] = React.useState(defaultShow);
    const id = React.useId();

    function onClick() {
        setShow(!show);
    }

    React.useEffect(() => {
        setShow(defaultShow);
    }, [defaultShow] );

    return {
        show,
        setShow,
        id,
        buttonProps: {
            onClick : press? undefined : onClick,
            onPress : press? onClick : undefined,
            "aria-controls": id,
            "aria-expanded": show ? true : false
        },
        collapseProps: {
            id,
            show
        }
    };
}
// React.HTMLAttributes<HTMLDivElement>
interface AnimateCollapseProps extends React.HTMLProps<HTMLDivElement>{
    /** A unique id required for accessibility purposes. */
    id: string;
    /** Controls whether the children should be visible */
    show: boolean;
    /** Any element that you want to reveal */
    children: React.ReactNode;
    divStyle?: SerializedStyles;
}

/**
 * Hide and reveal content with an animation. Supports dynamic
 * heights.
 */
export const AnimateCollapse = ({
                                    children,
                                    id,
                                    show,
                                    divStyle,
                                    ...other
          }: (AnimateCollapseProps & React.HTMLAttributes<HTMLDivElement>)) => {
    const ref = React.useRef<HTMLDivElement | null>(null);
    const bounds = useMeasure(ref);
    const prevShow = usePrevious(show);
    //高度 动画副作用，导致计算问题？ 旧的to: { height: show ? bounds?.height : 0 },
    //这个height实际是一个对象的。  const { animationObj } = useSpring ；注意两个height不是一个东西。
    //这里useSpring({）的参数 实际是初始化用的，后面输出的对象height.value实际是动画编制的起始数值，而不是目标数值。
    //   React.useEffect(() => {
    //       if (show) {
    //           scrollIntoView(ref.current!, {
    //               behavior: 'smooth',
    //               scrollMode: 'if-needed',
    //           });
    //       }
    //   }, [show]);
    //[问题！] 这里的useSpring（）没有处理内部DIV高度变化的副作用；打印机场景div高度会缩放调整，应该被缩小的<animated.div却没有被缩小。
    const styles = useSpring({
        from: { height: 0 },
        to: { height: show ? bounds?.height : 0 },
        immediate: false,        //prevShow !== null && prevShow === show
        opacity: show ? 1 : 0,
    }) as any;
    const { height } =styles;
    //实际打印预览会捕获3次的render这里，Collapse-捕获height=，前面2次纸张缩放调整，缩小了，div就会变矮化，后面第三次却是屏幕的。
    //只好先预留大一点的高度。 有待改进。动画可能切割打印。
    const dynamicHeight = (bounds?.height > (height&&height.value)) ? (bounds?.height): (height&&height.value)>0? (height&&height.value) : undefined;

    //   style={{ height } as any}
    return (
        <div  css={[
            {
                height: show? dynamicHeight : 0,
                ["@media not print"]: {
                    display: show ?  undefined : 'none',
                },
                ["@media print"]: {
                    overflow: "hidden",
                },
            },
        ]}
        >
            <animated.div
                id={id}
                style={styles}
                css={{
                    willChange: "height, opacity",
                }}
                {...(other as any)}
            >

                <div ref={ref}  css={divStyle} >{children}</div>

            </animated.div>
        </div>
    );
};


interface CollapseProps extends React.HTMLAttributes<HTMLDivElement> {
    /** A unique id required for accessibility purposes. */
    id: string;
    /** Controls whether the children should be visible */
    show: boolean;
    /** Any element that you want to reveal */
    children: React.ReactNode;
    divStyle?: SerializedStyles;
    noAnimated?: boolean;
}

/**
 * Hide and reveal content with an animation. Supports dynamic heights.
 * 针对两种方式取舍display:'none', 和 show&& (<div>{children}</div>)比较，还是逻辑隐藏更好，若遇到耗时子组件时表现更好。
 */
export const Collapse = ({
                                                                     children,
                                                                     id,
                                                                     show,
                                                                     divStyle,
                                                                     noAnimated =false,
                                                                     ...other
                                                                 }:CollapseProps) => {

    return (
        <React.Fragment>
            {
                noAnimated? (
                        <div
                            id={id}
                            aria-hidden={!show}
                            css={{
                                display: show ?  undefined : 'none',
                                overflow: "hidden",
                            }}
                            {...other}
                        >
                            <LazyToggleViewer show={show} css={divStyle}>
                                {children}
                            </LazyToggleViewer>
                        </div>
                    )
                    :
                    (
                        <AnimateCollapse  id={id}  show={show}  divStyle={divStyle}>
                            {children}
                        </AnimateCollapse>
                    )
            }
        </React.Fragment>
    );
};

// Collapse.propTypes = {
//     children: PropTypes.node,
//     id: PropTypes.string.isRequired,
//     show: PropTypes.bool.isRequired
// };

export function usePrevious<T>(value: T) {
    const ref = React.useRef<T | null>(null);
    React.useEffect(() => {
        ref.current = value;
    }, [value]);
    return ref.current;
}

/* 比较优缺点：
{ show  && toLoad }   逻辑&&方式加载快，恢复显示每次都得再render！适合展示概率不算大的内容。
 display:'none' ,	 组件加载时慢，后面恢复显示就很快了，不会再render了！较适合大概率会呈现的内容。
visibility:'hidden'   切换不浪费时间，但界面空间上被占用，适用性不好。
*/
interface LazyToggleViewerProps {
    show: boolean;
    children?: React.ReactNode;
}
//切换隐藏显示得内容： 初始化show=false情形=启动懒加载。是取长补短的模式。
//若放在逻辑&&方式{show&&()}的组件下或路由后再进入呈现的子组件，就失去效果，父组件被要求重新渲染！就等于树的根节点决定枝条节点命运。
//这样来看该组件放在层级太低的组件位置，可能没有意义，要搞就得放在较高组件层次的位置上。路由RouterLink本身就是逻辑&&方式的。
export const LazyToggleViewer = ({
                                                                                     show,
                                                                                     children,
                                                                                     ...other
                                                                                 }:LazyToggleViewerProps) => {
    const refclc = React.useRef<HTMLDivElement>(null);
    const [mounted, setMounted] = React.useState(false);
    // React.useEffect(() => {
    //   if (show) {
    //       scrollIntoView(refclc.current!, {
    //         behavior: 'smooth',
    //         scrollMode: 'if-needed',
    //       });
    //   }
    // }, [show]);

    if(!mounted){
        if(show)  setMounted(true);
        //这里反正底下组件还未加载，可以这么做
        return  null;
    }
    return (
        <div   ref={refclc}
               css={{display: show ? undefined : 'none'}}
               {...other}
        >
            {children}
        </div>
    );
};

// LazyToggleViewer.propTypes = {
//     children: PropTypes.node,
//     show: PropTypes.bool.isRequired
// };

interface TestRenderLoadProps {
    loop?: number;
    num?: number;
    txt?: string;
}
//性能测试专用的。
export const TestRenderLoad = ({ loop=3000, num=4, txt='针对渲染次数做优化，性能测试专用的！', ...other   }:TestRenderLoadProps) => {
    console.log(txt);
    return (
        <div  css={{display: 'none'}}
              {...other}
        >
            {
                Array.from(new Array(loop)).map( (a,i)=>
                    {
                        return <Text key={i}>
                            {
                                Array.from(new Array(num)).map( (a,i)=>
                                    { return <Text key={i}>{txt}</Text>}
                                )
                            }
                        </Text>
                    }
                )
            }
        </div>
    );
};

